/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __LIB3D_PARTICLESYSTEM_H__
#define __LIB3D_PARTICLESYSTEM_H__

#include <xml/Object.h>
#include <xml/Stream.h>
#include "ParticleInitializer.h"
#include "ParticleEvent.h"
#include <vector>
#include <HardwareDependentObject.h>
#include <pictureformat/Loader.h>
#include <lib3d/hard/Manager.h>
#include "ParticleKeyFrame.h"
#include <math/Rect.h>
#include <lib3d/hard/BlendMode.h>

namespace liba
{
	namespace lib3d
	{
		namespace particles
		{
			class ParticleSystem : public xml::ObjectANSI, public HardwareDependentObject<hard::Manager, hard::Texture>
			{
			private:
				struct FrameTextureCoords
				{
					math::Vector2<Float> minCorner;
					math::Vector2<Float> maxCorner;
				};
			private:
				int                                               _referencesCounter;
				std::string                                       _name;
				std::vector<ParticleInitializer*>                 _initializers;
				KeyFrameMap                                       _keyFrames;
				Float                                             _particlesLifetime;
				Float                                             _emitterLifetime;
				size_t                                            _maxParticlesCount;
				Float                                             _emissionRate;
				size_t                                            _emissionCount;
				filesystem::String                                _texturePath;
				std::map<hard::Hardware*, pictureformat::Loader*> _pictureLoader;
				std::vector<FrameTextureCoords>                   _textureFrames;
				float                                             _frameDelayTime;
				math::Vector2<Float>                              _frameSize;
				bool                                              _framesExtracted;
				bool											  _activate;
				bool											  _useAtlas;
				hard::BlendMode									  _blendMode;
				bool                                              _parentToEmitter;
			private:
				virtual ~ParticleSystem();
				void add_event_type(const Atom & typeName);
				KeyFrameIterator find_base_keyframe(KeyFrameArray * keyFrames, Float time);
				KeyFrameIterator find_next_keyframe(KeyFrameArray * keyFrames, Float time, KeyFrameIterator baseKey);
				void load_texture(HardVector::iterator texture);
			protected:
				virtual void on_add_hardware(hard::Manager::hardware_type * hardware);
				virtual void on_save_nodes(xml::SaverBase * saver) const;
				virtual void on_save_attributes(xml::SaverBase * saver) const;
			public:
				ParticleSystem(const std::string & name);
				virtual ParticleSystem * retain();
				virtual void release();
				
				void set_particles_lifetime(Float newLifetime);
				Float get_particles_lifetime();
				
				void set_emitter_lifetime(Float newLifetime);
				Float get_emitter_lifetime();
				
				void set_max_particles_count(size_t maxParticles);
				size_t get_max_particles_count();
				
				void set_emission_rate(Float emissionRate);
				Float get_emission_rate();
				
				void set_emission_count(size_t emissionCount);
				size_t get_emission_count();
				
				void set_blend_mode(hard::BlendMode value);
				hard::BlendMode get_blend_mode() const;

				void get_texture_coords_for_time(Float time, math::Vector2<Float> * minCorner, math::Vector2<Float> * maxCorner);
				
				hard::Texture * get_particles_texture(hard::Hardware * hardware);
				void set_particles_texture(const filesystem::String & path);
				filesystem::String get_particles_texture_path();

				void initialize_particle(Particle * particle, const Matrix & emitterMatrix);
				size_t update_particles(Particle * particles, size_t count, Float delta_time);
				
				void add_particles_initializer(ParticleInitializer * initializer);
				void add_particles_event(Float time, ParticleEvent * event);
				void clear_particles_events();
				size_t get_particles_initializers_count();
				ParticleInitializer * get_particles_initializer(size_t index);
				size_t get_particles_events_count() const;
				ParticleEvent * get_particles_event(size_t index) const;
				float get_particles_event_time(size_t index) const;

				virtual bool on_node(xml::Provider * provider, const std::string & name);
				virtual bool on_attribute(xml::Provider * provider, const std::string & name, const std::string & value);

				void activateParticleSystem(bool _active) 
				{
					if (!_active)
					{
						deactivateParticleSystem(_active);
						return;
					}
					
					_activate = _active;
				}

				void deactivateParticleSystem(bool _active)
				{
					_activate = _active;
				}

				inline bool getActivate() { return _activate; }

				bool is_parented_to_emitter() const;
			};
		}
	}
}

using namespace liba;

#endif